import datetime
import json
import logging
import re

from config import envConfig, kqConfig, config, aiConfig
from instructions.help_instruction import HelpInstruction
from instructions.import_instruction import ImportInstruction
from instructions.import_msg_instruction import ImportMSGInstruction
from instructions.instruction import Instruction
from instructions.enum_types import InstructionType, ActionType
from instructions.other_instruction import OtherInstruction
from instructions.registry_instruction import RegistryInstruction
from instructions.report_today_instruction import ReportTodayInstruction
from instructions.static_instruction import StaticInstruction
from models import Chat, Users, Kumquats, Order
from tools import check_contain

logger = logging.getLogger(__name__)


class ChatHandler:
    """
    用于构建数据库中 chat 表的类，用于记录本次处理的信息，包括处理时间，处理流程，源数据。
    这个类的实例会被作为self参数传入各种指令处理的类中进行记录
    函数的调用规则：
    check_user_exists - 有些请求必须保证请求人已经注册成为用户，如果不是用户则发送相关的拒绝话语，
                        虽然这个函数放在这里似乎语义不太符合，额。。就是觉得放在这里比较方便
    add_actions - 用于记录流程，所有的流程都包含在instruction_type.py的两个enum类中。 第一个action必定记录处理的指令类型
    build_chat - 在最后调用，用于构建本次请求的所有信息，并存入数据库
    """

    def __init__(self, msg):
        self.msg = msg
        self.actions = []
        self.answer = ""
        self.role = "ANONYMOUS"
        self.user_id = 1
        self.nn = ""
        self.ai_answer = ""
        self.query = ""
        self.note = ""

    async def init_requestor(self):
        isUser, user = await self.check_user_exists()
        if isUser:
            self.user_id = user.id
            self.role = user.role
            self.nn = user.nickname
        logger.info("[DB] Init requestor info - user_id: {}, role: {}".format(self.user_id, self.role))

    async def check_user_exists(self, name=None):
        """
        检测用户是否存在，主要检查nickname这是唯一项
        # TODO：不知道能否使用name+nickname作为唯一，虽然数据库层面可以实现，但代码逻辑不好写，不能让用户每次都输入自己的注册名字。。。
        :param name: 暂时没用的
        :return:
        """
        logger.debug("[DB] check if user exists")
        # self.add_actions(ActionType.CHECK_USER_EXISTS)
        if self.msg.is_group:
            filters = {
                "nickname": self.msg.actual_user_nickname,
                "is_deleted": False
            }
        else:
            filters = {
                "nickname": self.msg.from_user_nickname,
                "is_deleted": False
            }
        if name:
            filters["name"] = name
        user = await Users.filter(**filters).first()
        if user:
            logger.debug("[DB] User exists")
            return True, user
        logger.debug("[DB] User does not exist")
        return False, None

    def check_permission(self, instruction: Instruction):
        self.add_actions(ActionType.CHECK_PERMISSION)
        if self.user_id == 0 or self.role == 'ADMIN':
            return True
        elif (self.user_id == 1 or self.role == 'ANONYMOUS') and \
                instruction.insType in config.ANONYMOUS_INSTRUCTION_WHITE_LIST:
            return True
        elif self.role == 'MANAGER' and instruction.insType not in config.ADMIN_GOD_INSTRUCTION_LIST:
            return True
        return False

    def add_actions(self, actions: InstructionType | ActionType):
        self.actions.append(actions.name)

    async def build_chat(self):
        """
        把解析步骤放入chat数据库，如果用户没有注册过，则用anonym替代
        :return:
        """
        await self.init_requestor()  # regenerate user info
        chat = Chat(
            user_id=self.user_id,
            query=self.query,
            query_time=datetime.datetime.fromtimestamp(self.msg.create_time),
            actions=str(self.actions),
            answer=self.answer,
            ai_answer=self.ai_answer,
            raw_msg=json.dumps(self.msg.__dict__, ensure_ascii=False, indent=4),
            note=self.note
        )
        await chat.save()
        logger.debug("[DB] chat saved")

    async def check_user_has_kumquat(self, k_name, k_format, force_flag=False):
        # self.add_actions(ActionType.CHECK_HAS_KUMQUAT_DATA)
        if force_flag:
            kumquat = await Kumquats.filter(name=k_name, format=k_format,  # 这里是保证用户用强行导入的方式优先使用自己的金桔数据
                                            users__id__in=[0, self.user_id], note='CURRENT').first()

        else:
            kumquat = await Kumquats.filter(name=k_name, format=k_format, users__id=self.user_id, note='CURRENT').first()
        if not kumquat:
            return False
        return kumquat

    async def get_users_kumquats(self, force_flag=False):
        self.add_actions(ActionType.GET_USER_KUMQUATS)
        if force_flag:
            kumquats = await Kumquats.filter(users_id__in=[0, self.user_id], note='CURRENT') \
                .all().order_by('name', 'format', 'users_id')
            # .values(*kqConfig.KUMQUATS_PUBLIC_COLUMNS)
        else:
            kumquats = await Kumquats.filter(users_id=self.user_id, note='CURRENT') \
                .all().order_by('name', 'format', 'users_id')
            # .values(*kqConfig.KUMQUATS_PUBLIC_COLUMNS)

        return kumquats

    async def filter_order_by_date(self,
                                   start=datetime.datetime.combine(datetime.datetime.now().date(), datetime.time.min),
                                   end=datetime.datetime.combine(datetime.datetime.now().date(), datetime.time.max)):
        """
        查询某个时间段的订单数据，这个hander太好用了
        :param start:
        :param end:
        :return:
        """
        self.add_actions(ActionType.FILTER_ORDER_DATE)
        orders = await Order.filter(book_date__gte=start, book_date__lte=end)\
            .order_by('book_date').prefetch_related('users', 'kumquats', 'customers')
        return orders


class WXHandler:
    def __init__(self, typeIn, content, isgroup, msg):
        self.type = typeIn
        self.raw_content = content
        self.content = self.filter_reference(content)
        self.isGroup = isgroup
        self.msg = msg
        self.chatDBHandler = ChatHandler(msg)
        self.chatDBHandler.query = content
        logger.info(f"[init handler] Received the content: {self.raw_content}, filter content: {self.content}")

    async def parse_intention(self):
        """
        Factory design pattern, 根据传入之类构造具体的指令处理流程。 正常来说，必须返回一个 Instruction 类型，不要偷懒就写string了！
        :return:
        """
        instruction = None
        if envConfig.CHECKSUM_KEYS in self.content:
            instruction = StaticInstruction(self.chatDBHandler, InstructionType.CHECKSUM_FILE,
                                            **{"fpath": self.content.replace(envConfig.CHECKSUM_KEYS, '')})
            self.chatDBHandler.add_actions(InstructionType.CHECKSUM_FILE)
        # ⚠️ 获得指令帮助
        elif check_contain(self.content, envConfig.HELP_KEYS):
            instruction = HelpInstruction(self.chatDBHandler, **{"keywords": self.content})
            self.chatDBHandler.add_actions(InstructionType.HELP)
        # ⚠️ 用户注册,或者检测注册信息，或者删除用户
        elif check_contain(self.content, envConfig.REGISTER_KEYS) or check_contain(self.content, envConfig.DELETE_KEYS):
            checkUser = await self.chatDBHandler.check_user_exists()
            if checkUser[0]:
                if not check_contain(self.content, envConfig.DELETE_KEYS):
                    instruction = StaticInstruction(self.chatDBHandler,
                                                    InstructionType.CHECK_USER_ALREADY_EXISTS,
                                                    **{"user": checkUser[1]})
                    self.chatDBHandler.add_actions(InstructionType.CHECK_USER_ALREADY_EXISTS)
                else:
                    instruction = StaticInstruction(self.chatDBHandler,
                                                    InstructionType.DELETE_USER,
                                                    **{"user": checkUser[1]})
                    self.chatDBHandler.add_actions(InstructionType.DELETE_USER)
            else:
                instruction = RegistryInstruction(self.chatDBHandler, self.content)
                self.chatDBHandler.add_actions(InstructionType.REGISTRY_USER)
                # 校验文件是否需要保存 DONE

        # ⚠️ 导入订单文件, 判断是否有导入指令，再判断是否包含引用信息  # TODO: DONE，初步简单测试通过
        elif check_contain(self.content, envConfig.IMPORT_EXCEL_KEYS) and \
                envConfig.CHECKSUM_KEYS not in self.content:
            if not self.is_contains_reference():
                self.chatDBHandler.note = "No reference found, return Help file."
                instruction = HelpInstruction(self.chatDBHandler, **{"keywords": InstructionType.IMPORT_EXCEL_ORDER})
                return instruction
            if check_contain(self.content, envConfig.FORCE_IMPORT_KEYS):
                instruction = ImportInstruction(self.chatDBHandler, self.raw_content, True)  # 这里需要把引用也传递过去
            else:
                instruction = ImportInstruction(self.chatDBHandler, self.raw_content)  # 这里需要把引用也传递过去

        # ⚠️ 获得模板文件  # TODO：如果是 【模板文件列表/模板有哪些/模板列表】的关键字，应该返回当前模板文件列表
        elif check_contain(self.content, envConfig.GET_TEMPLATE_KEYS) and \
                envConfig.CHECKSUM_KEYS not in self.content:
            instruction = StaticInstruction(self.chatDBHandler, InstructionType.GET_TEMPLATE_KEYS,
                                            **{"tpl": self.content})
            self.chatDBHandler.add_actions(InstructionType.GET_TEMPLATE_KEYS)
        # ⚠️ 文字插入订单  # TODO: 待完善 优先级⭐⭐⭐⭐ TO TEST
        elif check_contain(self.content, envConfig.ADD_ORDER_KEYS):
            instruction = ImportMSGInstruction(self.chatDBHandler, self.content)
            self.chatDBHandler.add_actions(InstructionType.IMPORT_BY_WORDS)

        # ⚠️ 订单修改  TODO: 待完善 优先级⭐
        elif check_contain(self.content, envConfig.CHANGE_ORDER_KEYS):
            logger.warning("Not implemented!")
        # ⚠️ 汇总当日订单  TODO: 待完善 优先级⭐⭐⭐⭐
        elif check_contain(self.content, envConfig.REPORT_TODAY_KEYS):
            instruction = ReportTodayInstruction(self.chatDBHandler, self.content)
            self.chatDBHandler.add_actions(InstructionType.REPORT_TODAY)

        # ⚠️ 根据条件汇总个人销售成绩  TODO: 待完善 优先级⭐
        elif check_contain(self.content, envConfig.REPORT_ORDERS_KEYS):
            logger.warning("Not implemented!")
        # ⚠️ 检查当天自己的订单  TODO：待完善 优先级⭐⭐
        elif check_contain(self.content, envConfig.CHECK_MYTODAY_KEYS):
            logger.warning("Not implemented!")

        else:
            # ⚠️ 交给AI, TODO: 不过没有上下文会有点不明所以, 估计要搞个聊天记录的表格放在数据库，但优先级不高
            instruction = OtherInstruction(self.chatDBHandler, self.content)
            logger.warning("[parse_intention] 不在指令集中，让AI随便说点吧。")
            self.chatDBHandler.add_actions(InstructionType.BAVARDER)
        return instruction

    def is_contains_reference(self):
        """
        判断是否是引用的文字
        :return:
        """
        if "」\n- - - - - - -" in self.raw_content or "」 - - - - - - -" in self.raw_content:
            return True
        return False

    @staticmethod
    def filter_reference(text: str) -> str:
        """
        过滤引用
        :param text:
        :return:
        """
        cleaned_text = re.sub(r'「.*?」', '', text)
        # cleaned_text = re.sub(r'-+', '', cleaned_text).strip()
        cleaned_text = cleaned_text.replace(' - - - - - - -', '')
        return cleaned_text
